var MixlyBU = {};

var downloadShell = null;
var iconv = MixlyEnv.isClient && require('iconv-lite');

MixlyBU.uploading = false;

MixlyBU.burning = false;

MixlyBU.uploadType = "";

MixlyBU.uploadVolumeName = "";

MixlyBU.uploadFileType = "";

MixlyBU.uploadFilePath = "";

MixlyBU.uploadCommand = "";

MixlyBU.burnCommand = "";

MixlyBU.uploadAndCopyLib = false;

function isExitsFunction(funcName) {
    try {
        if (typeof (eval(funcName)) == "function") {
            return true;
        }
    } catch (e) { }
    return false;
}

function replaceWithReg(str, newData, type) {
    if (str) {
        try {
            var re = new RegExp("{[\s]*" + type + "[\s]*}", "gim");
            return str.replace(re, newData);
        } catch (e) {
            return str;
        }
    }
    return str;
}

/**
* @function 读取json并获取相关数据
* @description 读取MixlyUrl.BOARD_CONFIG，从中获取uploadFilePath、uploadFileType、uploadVolumeName、burnCommand、uploadCommand
* @return void
*/
MixlyBU.readConfigAndSet = function () {
    if (MixlyUrl.BOARD_CONFIG?.upload?.filePath) {
        MixlyBU.uploadFilePath = MixlyUrl.BOARD_CONFIG.upload.filePath;
        try {
            MixlyBU.uploadFilePath = MixlyBU.uploadFilePath.replace(/\\/g, "/");
        } catch (e) {
            console.log(e);
        }
        MixlyBU.uploadFilePath = replaceWithReg(MixlyBU.uploadFilePath, MixlyEnv.clientPath, "path");
        MixlyBU.uploadFilePath = replaceWithReg(MixlyBU.uploadFilePath, MixlyEnv.indexPath, "indexPath");
        if (MixlyBU.uploadFilePath.toLowerCase().indexOf(".py") != -1) {
            MixlyBU.uploadFileType = "py";
        } else {
            MixlyBU.uploadFileType = "hex";
        }
    } else {
        MixlyBU.uploadFileType = "py";
        MixlyBU.uploadFilePath = MixlyEnv.clientPath + "/" + "mpbuild/main.py";
    }
    if (MixlyUrl.BOARD_CONFIG?.upload?.type) {
        MixlyBU.uploadType = MixlyUrl.BOARD_CONFIG.upload.type;
    } else {
        MixlyBU.uploadType = "ampy";
    }
    if (MixlyBU.uploadType == "volumeLabel") {
        if (MixlyEnv.currentPlatform == "darwin" || MixlyEnv.currentPlatform == "linux") {
            if (typeof MixlyUrl.BOARD_CONFIG.upload?.volumeName == "string") {
                MixlyBU.uploadVolumeName = MixlyUrl.BOARD_CONFIG.upload.volumeName;
            } else if (typeof MixlyUrl.BOARD_CONFIG.upload?.volumeName == "object") {
                MixlyBU.uploadVolumeName = MixlyUrl.BOARD_CONFIG.upload.volumeName[0];
                for (var i = 1; i < MixlyUrl.BOARD_CONFIG.upload.volumeName.length; i++) {
                    MixlyBU.uploadVolumeName += "/" + MixlyUrl.BOARD_CONFIG.upload.volumeName[i];
                }
            } else {
                MixlyBU.uploadVolumeName = "CIRCUITPY";
            }
        } else {
            if (typeof MixlyUrl.BOARD_CONFIG.upload?.volumeName == "string") {
                MixlyBU.uploadVolumeName = "VolumeName='" + MixlyUrl.BOARD_CONFIG.upload.volumeName + "'";
            } else if (typeof MixlyUrl.BOARD_CONFIG.upload?.volumeName == "object") {
                MixlyBU.uploadVolumeName = "VolumeName='" + MixlyUrl.BOARD_CONFIG.upload.volumeName[0] + "'";
                for (var i = 1; i < MixlyUrl.BOARD_CONFIG.upload.volumeName.length; i++) {
                    MixlyBU.uploadVolumeName += " or VolumeName='" + MixlyUrl.BOARD_CONFIG.upload.volumeName[i] + "'";
                }
            } else {
                MixlyBU.uploadVolumeName = "VolumeName='CIRCUITPY'";
            }
        }
    }
    if (MixlyUrl.BOARD_CONFIG?.burn?.command) {
        MixlyBU.burnCommand = MixlyUrl.BOARD_CONFIG.burn.command;
        try {
            MixlyBU.burnCommand = MixlyBU.burnCommand.replace(/\\/g, "/");
        } catch (e) {
            console.log(e);
        }
        var pyToolName = ["esptool", "kflash", "stm32loader", "stm32bl"];
        var pyToolPath = "{path}/mixpyBuild/win_python3/Lib/site-packages/"
        if (MixlyEnv.currentPlatform == "darwin" || MixlyEnv.currentPlatform == "linux") {
            pyToolPath = "{path}/pyTools/";
        }
        for (var i = 0; i < pyToolName.length; i++) {
            if (MixlyBU.burnCommand.indexOf("\"") != -1) {
                MixlyBU.burnCommand = replaceWithReg(MixlyBU.burnCommand, MixlyEnv.python3Path + "\" \"" + pyToolPath + pyToolName[i] + ".py", pyToolName[i]);
            } else {
                MixlyBU.burnCommand = replaceWithReg(MixlyBU.burnCommand, MixlyEnv.python3Path + " " + pyToolPath + pyToolName[i] + ".py", pyToolName[i]);
            }
        }
        MixlyBU.burnCommand = replaceWithReg(MixlyBU.burnCommand, MixlyEnv.clientPath, "path");
        MixlyBU.burnCommand = replaceWithReg(MixlyBU.burnCommand, MixlyEnv.indexPath, "indexPath");
    }
    if (MixlyUrl.BOARD_CONFIG?.upload?.command) {
        MixlyBU.uploadCommand = MixlyUrl.BOARD_CONFIG.upload.command;
        try {
            MixlyBU.uploadCommand = MixlyBU.uploadCommand.replace(/\\/g, "/");
        } catch (e) {
            console.log(e);
        }
        if (MixlyEnv.currentPlatform == "darwin" || MixlyEnv.currentPlatform == "linux") {
            MixlyBU.uploadCommand = replaceWithReg(MixlyBU.uploadCommand, MixlyEnv.python3Path + "\" \"{path}/pyTools/ampy/cli.py", "ampy");
        } else {
            MixlyBU.uploadCommand = replaceWithReg(MixlyBU.uploadCommand, MixlyEnv.python3Path + "\" \"{path}/mixpyBuild/win_python3/Lib/site-packages/ampy/cli.py", "ampy");
        }
        console.log(MixlyBU.uploadCommand);
        MixlyBU.uploadCommand = replaceWithReg(MixlyBU.uploadCommand, MixlyEnv.clientPath, "path");
        MixlyBU.uploadCommand = replaceWithReg(MixlyBU.uploadCommand, MixlyEnv.indexPath, "indexPath");
    }
    if (MixlyUrl.BOARD_CONFIG?.upload?.copyLib) {
        MixlyBU.uploadAndCopyLib = MixlyUrl.BOARD_CONFIG.upload.copyLib;
    }
}

try {
    /*
    if (MixlyEnv.currentPlatform === "darwin" || MixlyEnv.currentPlatform === "linux") {
        child_process.exec('which python3', function (error, stdout, stderr) {
            if (error || stdout == null) {
                MixlyEnv.python3Path = '/usr/local/bin/python3';
            } else {
                MixlyEnv.python3Path = stdout.replace("\n", "");
            }
            console.log(MixlyEnv.python3Path);
            MixlyBU.readConfigAndSet();
        });
    } else {
        MixlyBU.readConfigAndSet();
    }
    */
    if (MixlyEnv.currentPlatform !== "win32") {
        if (nodeFs.existsSync("/usr/local/bin/python3")) {
            MixlyEnv.python3Path = '/usr/local/bin/python3';
        }
        console.log(MixlyEnv.python3Path);
    }
} catch (e) {
    console.log(e);
}

MixlyBU.readConfigAndSet();

/**
* @function 烧录或上传时判断是否有多个设备
* @description 判断是否有多个设备，如果存在，则弹出设备选择框，若不存在，则开始一个烧录或上传过程
* @param burn {Boolean} 烧录或上传，true - 烧录，false - 上传
* @param addAllOption {Boolean} 是否在串口下拉框内添加【全部】选项，true - 添加，false - 不添加
* @return void
*/
MixlyBU.checkNumOfSerialPort = function (ports, burn, addAllOption) {
    var form = layui.form;
    const $devNames = $('#select_serial_device');
    var old_Device = $('#select_serial_device option:selected').val();
    $devNames.empty();
    _.map(v => {
        if (v != undefined && v != '') {
            if (`${v}` == old_Device) {
                $devNames.append($(`<option value="${v}" selected>${v}</option>`));
            } else {
                $devNames.append($(`<option value="${v}">${v}</option>`));
            }
        }
    }, ports);

    form.render();

    var device_num = document.getElementById("select_serial_device").length;

    if (device_num > 1 && addAllOption) {
        if (old_Device == 'all') {
            $devNames.append('<option value="all" selected>' + indexText['全部'] + '</option>');
        } else {
            $devNames.append('<option value="all">' + indexText['全部'] + '</option>');
        }
        device_num++;
    } else {
        addAllOption = false;
    }

    form.render();

    if (device_num > addAllOption) {
        if (burn)
            MixlyBU.burning = true;
        else
            MixlyBU.uploading = true;
    }
    if (device_num == addAllOption) {
        layer.msg(indexText['无可用设备'] + '!', {
            time: 1000
        });
        MixlyBU.burning = false;
        MixlyBU.uploading = false;
    } else if (device_num == 1 + addAllOption) {
        layui.use('layer', function () {
            var layer = layui.layer;
            layer.open({
                type: 1,
                title: (burn ? indexText['烧录中'] + '...' : indexText['上传中'] + '...'),
                content: $('#webusb-flashing'),
                closeBtn: 0,
                success: function () {
                    $(".layui-layer-page").css("z-index","198910151");
                },
                end: function () {
                    document.getElementById('webusb-flashing').style.display = 'none';
                    $(".layui-layer-shade").css("display","none");
                }
            });
            var com_data = $('#select_serial_device option:selected').val();
            if (burn) {
                MixlyBU.burnByCmd(com_data);
            } else {
                MixlyBU.uploadByCmd(com_data);
            }
        });
    } else {
        layui.use(['layer', 'form'], function () {
            var layer = layui.layer;
            layer.open({
                type: 1,
                id: "serialSelect",
                title: indexText['检测到多个串口，请选择：'],
                area: ['350px', '150px'],
                content: $('#serial-device-form'),
                closeBtn: 0,
                success: function (layero) {
                    document.getElementById("serialSelect").style.height = "195px";
                    $(".layui-layer-page").css("z-index","198910151");
                },
                end: function () {
                    document.getElementById('serial-device-form').style.display = 'none';
                    $(".layui-layer-shade").css("display","none");
                }
            });
        });
    }
}

MixlyBU.checkNumOfDisks = function (stdout, path, pyCode, portSelect, addAllOption) {
    if (stdout.indexOf(":") != stdout.lastIndexOf(":")) {
        layer.closeAll('page');
        document.getElementById('webusb-flashing').style.display = 'none';
        layui.use(['layer', 'form'], function () {
            var layer = layui.layer;
            layer.open({
                type: 1,
                id: "serialSelect",
                title: indexText['检测到多个同类型设备，请选择：'],
                area: ['350px', '150px'],
                content: $('#serial-device-form'),
                closeBtn: 0,
                success: function (layero) {
                    document.getElementById("serialSelect").style.height = "195px";
                    $(".layui-layer-page").css("z-index","198910151");
                },
                end: function () {
                    document.getElementById('serial-device-form').style.display = 'none';
                    $(".layui-layer-shade").css("display","none");
                }
            });
        });
    } else {
        layui.use('layer', function () {
            var layer = layui.layer;
            layer.open({
                type: 1,
                title: indexText['上传中'] + '...',
                content: $('#webusb-flashing'),
                closeBtn: 0,
                success: function () {
                    var wmicResult = stdout;
                    wmicResult = wmicResult.replace(/\s+/g, "");
                    wmicResult = wmicResult.replace("DeviceID", "");
                    // wmicResult = 'G:K:F:';
                    let result = wmicResult.split(':');
                    console.log(result);

                    var pathAdd = (MixlyEnv.currentPlatform == "win32") ? ':' : '';
                    if (wmicResult.indexOf(":") != wmicResult.lastIndexOf(":")) {
                        var form = layui.form;
                        var device_Names = $('#select_serial_device');
                        var old_Device = $('#select_serial_device option:selected').val();
                        device_Names.empty();
                        for (var i = 0; i < result.length; i++) {
                            if (result[i]) {
                                if (old_Device == result[i] + pathAdd) {
                                    device_Names.append('<option value="' + result[i] + pathAdd + '" selected>' + result[i] + pathAdd + '</option>');
                                } else {
                                    device_Names.append('<option value="' + result[i] + pathAdd + '">' + result[i] + pathAdd + '</option>');
                                }
                            }
                        }
                        if (addAllOption) {
                            if (old_Device == 'all') {
                                device_Names.append('<option value="all" selected>' + indexText['全部'] + '</option>');
                            } else {
                                device_Names.append('<option value="all">' + indexText['全部'] + '</option>');
                            }
                        }
                        form.render();
                        return;
                    }
                    MixlyBU.writeAndCopyFile(path, result[0] + pathAdd + "/" + basename(path), pyCode, portSelect);
                },
                end: function () {
                    layer.closeAll('page');
                    document.getElementById('webusb-flashing').style.display = 'none';
                    $(".layui-layer-shade").css("display","none");
                }
            });
        });
    }
    
}

MixlyBU.writeAndCopyFile = function (writeFilePath, copyFilePath, pyCode, portSelect) {
    var code = "";
    if (pyCode) {
        code = MixlyFile.getPy();
    } else {
        code = MixlyFile.getHex();
    }

    if (MixlyBU.uploadAndCopyLib) {
        let oldLibPath = MixlyBU.uploadFilePath.substring(0, MixlyBU.uploadFilePath.lastIndexOf('/'));
        let newLibPath = copyFilePath.substring(0, copyFilePath.lastIndexOf('/'));
        let pyFileArr = MixlyBU.copyLib(code);
        console.log(oldLibPath);
        console.log(newLibPath);
        console.log(pyFileArr);
        copyFileWithName(oldLibPath, newLibPath, pyFileArr);
    }

    MixlyBU.writeFile(writeFilePath, code, function (err) {
        //写入文件出错时执行
        MixlyBU.writeFile(copyFilePath, code, MixlyBU.errFunc, MixlyBU.doFunc, portSelect)
    }, function () {
        MixlyBU.copyFile(writeFilePath, copyFilePath, function () {
            //写入文件出错时执行
            MixlyBU.writeFile(copyFilePath, code, MixlyBU.errFunc, MixlyBU.doFunc, portSelect)
        }, MixlyBU.doFunc, portSelect);
    }, portSelect);
}

MixlyBU.writeFile = function (path, data, errFunc, doFunc, portSelect) {
    nodeFs.writeFile(path, data, 'utf8', function (err) {
        //如果err=null，表示文件使用成功，否则，表示希尔文件失败
        if (err) {
            errFunc(err);
        } else if (!MixlyBU.uploading) {
            layer.closeAll('page');
            document.getElementById('webusb-flashing').style.display = 'none';
            return;
        } else {
            doFunc(portSelect);
        }
    })
}

MixlyBU.copyFile = function (oldPath, newPath, errFunc, doFunc, portSelect) {
    nodeFs.copyFile(oldPath, newPath, (err) => {
        if (err) {
            errFunc(err);
        } else if (!MixlyBU.uploading) {
            layer.closeAll('page');
            document.getElementById('webusb-flashing').style.display = 'none';
            return;
        } else {
            doFunc(portSelect);
        }
        MixlyBU.uploading = false;
    });
}

MixlyBU.errFunc = function (err) {
    //写入文件出错时执行
    layer.closeAll('page');
    document.getElementById('webusb-flashing').style.display = 'none';
    layer.msg(indexText['写文件出错了，错误是：'] + err, {
        time: 1000
    });
    MixlyStatusBar.setValue(indexText['写文件出错了，错误是：'] + err + '\n', true);
    MixlyStatusBar.show(1);
    console.log(indexText['写文件出错了，错误是：'] + err);
    MixlyBU.uploading = false;
}

MixlyBU.doFunc = function (portSelect) {
    //写入文件成功时执行
    layer.closeAll('page');
    document.getElementById('webusb-flashing').style.display = 'none';
    layer.msg(indexText['上传成功'] + '!', {
        time: 1000
    });
    console.log('ok');
    MixlyStatusBar.show(1);
    MixlySerial.refreshPortList(portSelect, function () {
        MixlySerial.updateSelectBox(MixlySerial.portList);
        MixlySerial.connectPort();
        MixlySerial.writeCtrlD();
    });
    MixlyBU.uploading = false;
}

/**
* @function 上传代码
* @description 通过盘符名称获取盘符号并拷贝某一文件（.hex | .py）到此盘符下
* @param VolumeName {String} 所要查找盘符的名称
* @param path {String} 所要拷贝文件的路径
* @param pyCode {Boolean} 上传文件为hex或py，true - 上传文件为py，false - 上传文件为hex
* @param portSelect {Array | String} 通过串口的VID和PID获取对应串口，当为all时，则获取全部串口
* @param addAllOption {Boolean} 是否在串口下拉框内添加【全部】选项，true - 添加，false - 不添加
* @return void
*/
MixlyBU.uploadWithVolumeName = function (VolumeName, path, pyCode, portSelect, addAllOption) {
    if (MixlyEnv.currentPlatform == "darwin" || MixlyEnv.currentPlatform == "linux") {
        var diskPath = '/Volumes/';
        if (MixlyEnv.currentPlatform == "linux") {
            diskPath = '/media/' + nodeOs.userInfo().username + '/';
        }
        var stdout = '';
        var result = null;
        result = VolumeName.split('/');
        var deviceNum = 0;
        for (var i = 0; i < result.length; i++) {
            if (result[i] == '') continue;
            for (var j = 0; ; j++) {
                try {
                    if (nodeFs.statSync(diskPath + result[i] + (j == 0 ? '' : (' ' + j))).isDirectory()) {
                        stdout += diskPath + result[i] + (j == 0 ? '' : (' ' + j)) + ':';
                        deviceNum++;
                    } else {
                        break;
                    }
                } catch (e) {
                    break;
                }
            }
        }
        if (deviceNum == 0) {
            layer.msg(indexText['无可用设备'] + '!', {
                time: 1000
            });
            MixlyBU.uploading = false;
            return;
        }
        MixlyBU.checkNumOfDisks(stdout, path, pyCode, portSelect, addAllOption);
    } else {
        child_process.exec('wmic logicaldisk where "' + VolumeName + '" get DeviceID', function (err, stdout, stderr) {
            if (err || stderr) {
                layer.closeAll('page');
                document.getElementById('webusb-flashing').style.display = 'none';
                console.log("root path open failed" + err + stderr);
                layer.msg(indexText['无可用设备'] + '!', {
                    time: 1000
                });
                MixlyBU.uploading = false;
                return;
            }
            MixlyBU.checkNumOfDisks(stdout, path, pyCode, portSelect, addAllOption);
        });
    }
}

/**
* @function 上传代码
* @description 通过下拉列表获取盘符号并拷贝某一文件（.hex | .py）到此盘符下
* @param path {String} 所要拷贝文件的路径
* @param pyCode {Boolean} 上传文件为hex或py，true - 上传文件为py，false - 上传文件为hex
* @param portSelect {Array | String} 通过串口的VID和PID获取对应串口，当为all时，则获取全部串口
* @return void
*/
MixlyBU.uploadWithDropdownBox = async function (path, pyCode, portSelect) {
    layer.closeAll('page');
    document.getElementById('serial-device-form').style.display = 'none';
    MixlyBU.uploading = true;
    layui.use('layer', function () {
        var layer = layui.layer;
        layer.open({
            type: 1,
            title: indexText['上传中'] + '...',
            content: $('#webusb-flashing'),
            closeBtn: 0,
            success: function () {
                $(".layui-layer-page").css("z-index","198910151");
            },
            end: function () {
                layer.closeAll('page');
                document.getElementById('webusb-flashing').style.display = 'none';
                $(".layui-layer-shade").css("display","none");
            }
        });
    });

    if (MixlySerial.object && MixlySerial.object.isOpen) {
        await MixlyBU.reset();
    }

    var code = "";
    if (pyCode) {
        code = MixlyFile.getPy();
    } else {
        code = MixlyFile.getHex();
    }

    nodeFs.writeFile(path, code, 'utf8', function (err) {
        //如果err=null，表示文件使用成功，否则，表示希尔文件失败
        if (err) {
            layer.closeAll('page');
            document.getElementById('webusb-flashing').style.display = 'none';
            layer.msg(indexText['写文件出错了，错误是：'] + err, {
                time: 1000
            });
            MixlyStatusBar.setValue(indexText['写文件出错了，错误是：'] + err + '\n', true);
            MixlyStatusBar.show(1);
            MixlyBU.uploading = false;
            return;
        } else if (!MixlyBU.uploading) { //如果检测到用户取消上传，则隐藏上传框
            layer.closeAll('page');
            document.getElementById('webusb-flashing').style.display = 'none';
            MixlyBU.uploading = false;
            return;
        } else {
            var device_values = $.map($('#select_serial_device option'), function (ele) {
                return ele.value;
            });
            var device_num = device_values.length;
            var device_select_name = $('#select_serial_device option:selected').val();
            if (device_select_name == "all") {
                var upload_finish_num = 0;
                for (var i = 0; i < device_num; i++) {
                    if (device_values[i] == "all") {
                        continue;
                    }
                    if (MixlyBU.uploadAndCopyLib) {
                        let oldLibPath = MixlyBU.uploadFilePath.substring(0, MixlyBU.uploadFilePath.lastIndexOf('/'));
                        let newLibPath = device_values[i] + "/";
                        let pyFileArr = MixlyBU.copyLib(code);
                        copyFileWithName(oldLibPath, newLibPath, pyFileArr);
                    }
                    nodeFs.copyFile(path, device_values[i] + "/" + basename(path), (err) => {
                        //layer.closeAll('page');
                        //document.getElementById('webusb-flashing').style.display = 'none';
                        upload_finish_num++;
                        if (err) {
                            layer.msg(indexText['写文件出错了，错误是：'] + err, {
                                time: 1000
                            });
                            MixlyStatusBar.setValue(indexText['写文件出错了，错误是：'] + err + '\n', true);
                            MixlyStatusBar.show(1);
                        } else if (MixlyBU.uploading) {
                            if (upload_finish_num >= device_num - 1) {
                                layer.msg(indexText['上传成功'] + '!', {
                                    time: 1000
                                });
                                MixlyStatusBar.show(1);
                                MixlySerial.refreshPortList(portSelect, function () {
                                    MixlySerial.updateSelectBox(MixlySerial.portList);
                                    MixlySerial.connectPort();
                                    MixlySerial.writeCtrlD();
                                });
                                MixlyBU.uploading = false;
                            }
                        }
                        if (upload_finish_num >= device_num - 1) {
                            layer.closeAll('page');
                            document.getElementById('webusb-flashing').style.display = 'none';
                        }
                    });
                }
            } else {
                if (MixlyBU.uploadVolumeName == "") {
                    MixlyBU.runCmd(false, device_select_name, MixlySerial.uploadPortType);
                } else {
                    if (MixlyBU.uploadAndCopyLib) {
                        let oldLibPath = MixlyBU.uploadFilePath.substring(0, MixlyBU.uploadFilePath.lastIndexOf('/'));
                        let newLibPath = device_select_name + "/";
                        let pyFileArr = MixlyBU.copyLib(code);
                        copyFileWithName(oldLibPath, newLibPath, pyFileArr);
                    }
                    nodeFs.copyFile(path, device_select_name + "/" + basename(path), (err) => {
                        layer.closeAll('page');
                        document.getElementById('webusb-flashing').style.display = 'none';
                        if (err) {
                            layer.msg(indexText['写文件出错了，错误是：'] + err, {
                                time: 1000
                            });
                            MixlyStatusBar.setValue(indexText['写文件出错了，错误是：'] + err + '\n', true);
                            MixlyStatusBar.show(1);
                        } else if (MixlyBU.uploading) {
                            layer.msg(indexText['上传成功'] + '!', {
                                time: 1000
                            });
                            MixlyStatusBar.show(1);
                            MixlySerial.refreshPortList(portSelect, function () {
                                MixlySerial.updateSelectBox(MixlySerial.portList);
                                MixlySerial.connectPort();
                                MixlySerial.writeCtrlD();
                            });
                        }
                        MixlyBU.uploading = false;
                    });
                }
            }

        }
    })
}

function basename(str) {
    var idx = 0;
    idx = str.lastIndexOf('/')
    idx = idx > -1 ? idx : str.lastIndexOf('/')
    if (idx < 0) {
        return str
    }
    return str.substring(idx + 1);
}

/**
* @function 取消烧录或上传
* @description 取消烧录或上传过程
* @return void
*/
MixlyBU.cancel = function () {
    layer.closeAll('page');
    document.getElementById('serial-device-form').style.display = 'none';
    document.getElementById('webusb-flashing').style.display = 'none';
    if (downloadShell) {
        downloadShell.stdout.end();
        //downloadShell.stdin.end();
        downloadShell.kill("SIGTERM");
        downloadShell = null;
    }
    if (MixlyBU.uploading) {
        MixlyBU.uploading = false;
        layer.msg(indexText['已取消上传'], {
            time: 1000
        });
    } else if (MixlyBU.burning) {
        MixlyBU.burning = false;
        layer.msg(indexText['已取消烧录'], {
            time: 1000
        });
    }
}

/**
* @function 开始烧录
* @description 开始一个烧录过程
* @param portSelect {Array | String} 通过串口的VID和PID获取对应串口，当为all时，则获取全部串口
* @return void
*/
MixlyBU.burn = function (portSelect) {
    MixlySerial.initPortList(portSelect, ports => {
        MixlyBU.checkNumOfSerialPort(ports, true, true);
    });
}

/**
* @function 判断当前环境，以开始一个烧录过程
* @description 判断当前环境(当前界面处在Mixly2.0或浏览器中)，以开始一个烧录过程
* @return void
*/
MixlyBU.initBurn = function () {
    if (MixlyBU.burning) return;
    MixlyStatusBar.setValue('', true);
    if (MixlyEnv.isClient) {
        MixlyStatusBar.taChange("output");
        MixlyBU.burning = true;
        MixlyBU.burn(MixlySerial.burnPortType);
    } else {
        if (isExitsFunction("MixlyWebBU.burn")) {
            MixlyWebBU.burn();
        } else {
            alert(indexText['此功能暂不可用']);
        }

        //webBurn("../../../../cpBuild/ESP32S2_MixGoCE/mixgoce.bin", false);
    }
}

/**
* @function 判断当前环境，以开始一个上传过程
* @description 判断当前环境(当前界面处在Mixly2.0或浏览器中)，以开始一个上传过程
* @return void
*/
MixlyBU.initUpload = function () {
    if (MixlyBU.uploading) return;
    MixlyStatusBar.setValue('', true);
    if (MixlySerial.object && MixlySerial.object.isOpen) {
        MixlySerial.object.close();
    }
    if (MixlyEnv.isClient) {
        MixlyStatusBar.taChange("output");
        MixlyBU.burning = false;
        if (MixlyBU.uploadFileType == "hex") {
            MixlyBU.uploading = true;
            MixlyBU.uploadWithVolumeName(MixlyBU.uploadVolumeName, MixlyBU.uploadFilePath, false, MixlySerial.uploadPortType, true);
        } else {
            if (MixlyBU.uploadType == "volumeLabel") {
                MixlyBU.uploading = true;
                MixlyBU.uploadWithVolumeName(MixlyBU.uploadVolumeName, MixlyBU.uploadFilePath, true, MixlySerial.uploadPortType, true);
            } else {
                MixlySerial.initPortList(MixlySerial.uploadPortType, ports => {
                    MixlyBU.checkNumOfSerialPort(ports, false, true);
                });
            }
        }
    } else {
        if (isExitsFunction("MixlyWebBU.upload")) {
            MixlyWebBU.upload();
        } else {
            alert(indexText['此功能暂不可用']);
        }
    }
}

/**
* @function 判断当前环境，以开始一个上传过程
* @description 判断当前环境(当前界面处在Mixly2.0或浏览器中)，以开始一个上传过程，并从下拉列表中获取所选择的串口
* @return void
*/
MixlyBU.initWithDropdownBox = function () {
    layer.closeAll('page');
    document.getElementById('serial-device-form').style.display = 'none';
    if (MixlyBU.burning) {
        layui.use('layer', function () {
            var layer = layui.layer;
            layer.open({
                type: 1,
                title: indexText['烧录中'] + '...',
                content: $('#webusb-flashing'),
                closeBtn: 0,
                success: function () {
                    $(".layui-layer-page").css("z-index","198910151");
                },
                end: function () {
                    document.getElementById('webusb-flashing').style.display = 'none';
                    $(".layui-layer-shade").css("display","none");
                }
            });
            var portData = $('#select_serial_device option:selected').val();
            if (portData == 'all') {
                var device_values = $.map($('#select_serial_device option'), function (ele) {
                    return ele.value;
                });
                MixlyBU.burnByCmd(device_values[0], 0, device_values.length - 1);
            } else {
                //MixlyBU.esptoolBurn(com_data, MixlyBU.ESPTOOL_COMMAND[boardType]);
                MixlyBU.burnByCmd(portData);
            }
        });
    } else {
        if (MixlyBU.uploadFileType == "hex") {
            MixlyBU.uploadWithDropdownBox(MixlyBU.uploadFilePath, false, MixlySerial.uploadPortType);
        } else {
            if (MixlyBU.uploadVolumeName == "") {
                layui.use('layer', function () {
                    var layer = layui.layer;
                    layer.open({
                        type: 1,
                        title: indexText['上传中'] + '...',
                        content: $('#webusb-flashing'),
                        closeBtn: 0,
                        success: function () {
                            $(".layui-layer-page").css("z-index","198910151");
                        },
                        end: function () {
                            document.getElementById('webusb-flashing').style.display = 'none';
                            $(".layui-layer-shade").css("display","none");
                            
                        }
                    });
                    var portData = $('#select_serial_device option:selected').val();
                    if (portData == 'all') {
                        var device_values = $.map($('#select_serial_device option'), function (ele) {
                            return ele.value;
                        });
                        MixlyBU.uploadByCmd(device_values[0], 0, device_values.length - 1);
                    } else {
                        MixlyBU.uploadByCmd(portData);
                    }
                });
            } else {
                MixlyBU.uploadWithDropdownBox(MixlyBU.uploadFilePath, true, MixlySerial.uploadPortType);
            }
        }
    }
}

/**
* @function 运行cmd
* @description 通过所给串口运行用户提供的esptool的cmd指令
* @param com {Array | String} 所选择的串口
* @return void
*/
MixlyBU.burnByCmd = function (com, currentTimes = 0, totalTimes = 1) {
    MixlyStatusBar.show(1);
    MixlyBU.burning = true;
    if (MixlySerial.object && MixlySerial.object.isOpen) {
        MixlySerial.object.close();
    }
    MixlyBU.runCmd(true, com, MixlySerial.burnPortType, currentTimes, totalTimes);
}

async function sleep(ms) {
    return new Promise(resolve => setTimeout(resolve, ms));
}

MixlyBU.reset = async function () {
    for (var i = 0; ; i++) {
        if (MixlyUrl.BOARD_CONFIG?.upload?.reset[i]?.dtr
            || MixlyUrl.BOARD_CONFIG?.upload?.reset[i]?.rts) {
            var dtrValue = false;
            var rtsValue = false;
            if (String(MixlyUrl.BOARD_CONFIG?.upload?.reset[i]?.dtr).toLowerCase() == 'true') {
                dtrValue = true;
            }
            if (String(MixlyUrl.BOARD_CONFIG?.upload?.reset[i]?.rts).toLowerCase() == 'true') {
                rtsValue = true;
            }
            MixlySerial.object.set({
                brk: false,
                cts: false,
                dsr: false,
                dtr: dtrValue,
                rts: rtsValue
            }, error => {
                if (error && MixlySerial.object && MixlySerial.object.isOpen) {
                    MixlySerial.object.close();
                }
            });
        } else if (MixlyUrl.BOARD_CONFIG?.upload?.reset[i]?.sleep) {
            var sleepValue = parseInt(MixlyUrl.BOARD_CONFIG.upload.reset[i].sleep) || 100;
            await sleep(sleepValue);
        } else {
            break;
        }
    }
}

MixlyBU.copyLib = function (code) {
    var uploadLibPath = MixlyBU.uploadFilePath.substring(0, MixlyBU.uploadFilePath.lastIndexOf('/'));
    var uploadFileName = MixlyBU.uploadFilePath.substring(MixlyBU.uploadFilePath.lastIndexOf('/') + 1, MixlyBU.uploadFilePath.length);

    try {
        if (!nodeFs.existsSync(uploadLibPath)) {
            nodeFs.mkdirSync(uploadLibPath);
        }
        var libFiles = nodeFs.readdirSync(uploadLibPath);
        for (var i = 0; i < libFiles.length; i++) {
            if (libFiles[i] != uploadFileName) {
                try {
                    nodeFs.unlinkSync(uploadLibPath + '/' + libFiles[i]);
                } catch (e) {
                    console.log(e);
                }
            }
        }
    } catch (e) {
        console.log(e);
    }
    var pyFileArr = [];
    pyFileArr = MixlyBU.searchLibs(code, pyFileArr);
    return pyFileArr;
}

MixlyBU.searchLibs = function (code, libArr) {
    var uploadLibPath = MixlyBU.uploadFilePath.substring(0, MixlyBU.uploadFilePath.lastIndexOf('/'));
    var arrayObj = new Array();
    code.trim().split("\n").forEach(function (v, i) {
        arrayObj.push(v);
    });
    var moduleName = "";
    var pyFileArr = [];
    for (var i = 0; i < arrayObj.length; i++) {
        if (arrayObj[i].indexOf("from") == 0) {
            moduleName = arrayObj[i].substring(4, arrayObj[i].indexOf("import"));
            moduleName = moduleName.replace(/(^\s*)|(\s*$)/g, "");
        } else if (arrayObj[i].indexOf("import") == 0) {
            moduleName = arrayObj[i].substring(6);
            moduleName = moduleName.replace(/(^\s*)|(\s*$)/g, "");
        } else {
            continue;
        }
        var moduleArr = moduleName.split(",");
        for (var j = 0; j < moduleArr.length; j++) {
            moduleArr[j] = moduleArr[j].replace(/(^\s*)|(\s*$)/g, "");
            if (!libArr.includes(moduleArr[j] + '.py')) {
                try {
                    var stat = nodeFs.statSync(MixlyEnv.indexPath + '/build/lib/' + moduleArr[j] + '.py');
                    if (stat.isFile()) {
                        var oldLibPath = MixlyEnv.indexPath + '/build/lib/' + moduleArr[j] + '.py';
                        var newLibPath = uploadLibPath + '/' + moduleArr[j] + '.py';
                        MixlyStatusBar.addValue(indexText['拷贝库'] + ' ' + moduleArr[j] + '\n', true);
                        nodeFs.copyFileSync(oldLibPath, newLibPath);
                        pyFileArr.push(moduleArr[j] + ".py");
                        libArr.push(moduleArr[j] + ".py");
                    }
                } catch (e) {
                    console.log(e);
                }
            }
        }
    }
    for (var i = 0; i < pyFileArr.length; i++) {
        try {
            code = String(nodeFs.readFileSync(MixlyEnv.indexPath + '/build/lib/' + pyFileArr[i]));
            libArr = MixlyBU.searchLibs(code, libArr);
        } catch (e) {
            console.log(e);
        }
    }
    return libArr;
}

/**
* @function 运行cmd
* @description 通过所给串口运行用户提供的ampy的cmd指令
* @param com {Array | String} 所选择的串口
* @return void
*/
MixlyBU.uploadByCmd = async function (com, currentTimes = 0, totalTimes = 1) {
    var code = MixlyFile.getPy();
    MixlyStatusBar.show(1);
    MixlyBU.uploading = true;

    if (MixlySerial.object && MixlySerial.object.isOpen) {
        await MixlyBU.reset();
    }

    if (MixlyBU.uploadAndCopyLib) {
        MixlyBU.copyLib(code);
    }

    if (MixlySerial.object && MixlySerial.object.isOpen) {
        MixlySerial.object.close();
    }
    await sleep(100);

    nodeFs.writeFile(MixlyBU.uploadFilePath, code, 'utf8', function (err) {
        //如果err=null，表示文件使用成功，否则，表示希尔文件失败
        if (err) {
            layer.closeAll('page');
            document.getElementById('webusb-flashing').style.display = 'none';
            layer.msg(indexText['写文件出错了，错误是：'] + err, {
                time: 1000
            });
            MixlyStatusBar.setValue(indexText['写文件出错了，错误是：'] + err + '\n', true);
            MixlyStatusBar.show(1);
            console.log(indexText['写文件出错了，错误是：'] + err);
            MixlyBU.uploading = false;
            return;
        } else {
            MixlyStatusBar.addValue(indexText['上传中'] + '...\n', true);
            MixlyBU.runCmd(false, com, MixlySerial.uploadPortType, currentTimes, totalTimes);
        }
    })
}

/**
* @function 运行cmd
* @description 通过所给信息运行用户提供的cmd指令
* @param burn {Boolean} 烧录或上传，true - 烧录，false - 上传
* @param com {Array | String} 所选择的串口
* @param portSelect {Array | String} 通过串口的VID和PID获取对应串口，当为all时，则获取全部串口
* @return void
*/
MixlyBU.runCmd = function (burn, com, portSelect, currentTimes = 0, totalTimes = 1) {
    var now_command = "";
    try {
        if (burn)
            now_command = replaceWithReg(MixlyBU.burnCommand, com, "com");
        else
            now_command = replaceWithReg(MixlyBU.uploadCommand, com, "com");
    } catch (e) {
        console.log(e);
    }
    console.log(now_command);
    downloadShell = child_process.exec(now_command, function (error, stdout, stderr) {
        currentTimes++;
        if (error !== null) {
            try {
                error = decode(iconv.decode(iconv.encode(error, "iso-8859-1"), 'gbk'));
            } catch (e) {
                console.log(e);
            }
            console.log("exec error" + error);
            MixlyStatusBar.addValue(error + "\n", true);
            layer.closeAll('page');
            document.getElementById('webusb-flashing').style.display = 'none';
            MixlyBU.burning = false;
            MixlyBU.uploading = false;
            return;
        } else {
            if (currentTimes >= totalTimes) {
                MixlyBU.burning = false;
                MixlyBU.uploading = false;
                layer.msg((burn ? indexText['烧录成功'] + '！' : indexText['上传成功'] + '！'), {
                    time: 1000
                });
                if (portSelect && !burn) {
                    MixlyStatusBar.show(1);
                    MixlySerial.selectedPortName = com;
                    MixlySerial.refreshPortList(portSelect, function () {
                        MixlySerial.updateSelectBox(MixlySerial.portList);
                        MixlySerial.connectPort();
                        MixlySerial.writeCtrlD();
                    });
                }
            }
        }
        MixlyStatusBar.scrollToTheBottom();
        if (currentTimes >= totalTimes) {
            layer.closeAll('page');
            document.getElementById('webusb-flashing').style.display = 'none';
        } else {
            if (MixlyBU.uploading || MixlyBU.burning) {
                var device_values = $.map($('#select_serial_device option'), function (ele) {
                    return ele.value;
                });
                MixlyBU.runCmd(burn, device_values[currentTimes], portSelect, currentTimes, totalTimes);
            }
        }
    })

    downloadShell.stdout.on('data', function (data) {
        if (MixlyBU.uploading || MixlyBU.burning) {
            try {
                data = decode(iconv.decode(iconv.encode(data, "iso-8859-1"), 'gbk'));
            } catch (e) {
                console.log(e);
            }
            MixlyStatusBar.addValue(data, true);
        }
    });
}
